package domain;

import java.util.*;

import entities.concept.DomainConcept;
import entities.concept.DomainConceptEQ;
import entities.concept.DomainConceptLT;
import entities.concept.DomainConceptLTEQ;

@Deprecated
/**
 * No multi-graph is allowed, i.e., when multiple edges occur for any two vertices, 
 * the edges will be updated sequentially and only one edge will be maintained.
 * @author j55wu
 *
 */
public class FeatureGraph {

	public final static int INTEQ = 0;
	public final static int INTLT = -1;
	public final static int INTLTEQ = 1;

	private HashSet<Vertex> vertices;
	private HashSet<Edge> edges;
	private HashSet<String> domainStrings;
	private boolean initialClash = false;
	private HashMap<Object, Vertex> FV_MAP = new HashMap<Object, Vertex>();
	private static final boolean Early_Cut = true;

	public FeatureGraph() {
		this.vertices = new HashSet<Vertex>();
		this.edges = new HashSet<Edge>();
		this.domainStrings = new HashSet<String>();
	}

	public HashSet<Vertex> getVertices() {
		return this.vertices;
	}

	public HashMap<Object, Vertex> getFVMAP() {
		return this.FV_MAP;
	}

	public void buildGraph(HashSet<DomainConcept<?, ?>> dcs) {
		// build initial edges
		addEdge(dcs);
		// build domain concepts, then add more edges
		for (DomainConcept<?, ?> dc : dcs) {
			// if given string1 OP string2. then test if a clash exists.
			int count = 0;
			if (!dc.isLHSRole()) {
				count++;
				this.domainStrings.add(dc.getLHS().toString());
			}
			if (!dc.isRHSRole()) {
				count++;
				this.domainStrings.add(dc.getValue().toString());
			}
			if (count == 2) {
				// two strings
				String s1 = (String) dc.getLHS();
				String s2 = (String) dc.getValue();
				int res = s1.compareTo(s2);
				if (res == 0 && dc instanceof DomainConceptLT || res < 0
						&& dc instanceof DomainConceptEQ || res > 0) {
					initialClash = true;
					if (Early_Cut) {
						return;
					}
				}
			}
		}
		if (this.domainStrings.size() > 1) {
			addEdge(this.buildDomainConcepts(this.domainStrings));
		}
	}

	// avoid duplicate vertices for the same role/string!
	public Vertex shareVertex(Object f) {
		if (FV_MAP.containsKey(f)) {
			return FV_MAP.get(f);
		} else {
			return new Vertex(f, this);
		}
	}

	// find clashes in this graph
	public boolean clashFinder() {
		if (initialClash) {
			return true;
		}
		boolean found = false;
		for (Vertex v : this.vertices) {
			found |= v.computeLTClash(v);
			found |= v.computeEQClash(v);
			if (found) {
				break;
			}
		}
		return found;
	}

	public boolean addEdge(HashSet<DomainConcept<?, ?>> dcs) {
		boolean ret = true;
		for (DomainConcept<?, ?> dc : dcs) {
			ret &= this.addEdge(dc);
			/*
			 * if(dc instanceof DomainConceptEQ){ DomainConceptEQ de = new
			 * DomainConceptEQ(dc.getValue(), dc.getFeature()); ret &=
			 * this.addEdge(de); }
			 */
		}
		return ret;
	}

	// add a triple (op f k) to the graph, true if added successfully
	public boolean addEdge(DomainConcept<?, ?> dc) {
		Vertex src = this.shareVertex(dc.getLHS());
		Vertex dest = this.shareVertex(dc.getValue());
		int op = this.getLabel(dc);
		// duplicate edges
		Edge test = this.duplicateEdge(src, dest);
		if (test != null) {
			// System.out.println("Update edge:"+test.toString());
			if (this.opClash(test.label, op)) {
				initialClash = true;
				if (Early_Cut) {
					return false;
				}
			}
			// update the edges: remove old edge from vertices, and add this new
			// edge!
			this.edges.remove(test);
			src.getOutEdges().remove(test);
			dest.getInEdges().remove(test);
		}
		Edge e = new Edge(op, src, dest);
		this.edges.add(e);
		src.addOutEdge(e);
		dest.addInEdge(e);
		// if f=g, we also add g=f automatically
		if (dc instanceof DomainConceptEQ) {
			Edge test2 = this.duplicateEdge(dest, src);
			if (test2 != null) {
				if (this.opClash(test2.label, op)) {
					initialClash = true;
					if (Early_Cut) {
						return false;
					}
				}
				this.edges.remove(test2);
				dest.getOutEdges().remove(test2);
				src.getInEdges().remove(test2);
			}
			Edge e2 = new Edge(op, dest, src);
			this.edges.add(e2);
			dest.addOutEdge(e2);
			src.addInEdge(e2);
		}

		this.vertices.add(src);
		this.vertices.add(dest);
		return true;
	}

	/*
	 * clash if < -> = or = -> <, i.e., one is less than, the other is equal.
	 */
	public boolean opClash(int op1, int op2) {
		return op1 + op2 == -1;
	}

	public Edge duplicateEdge(Vertex src, Vertex dest) {
		for (Edge e : this.edges) {
			if (e.identicalVertices(src, dest)) {
				return e;
			}
		}
		return null;
	}

	public int getLabel(DomainConcept<?, ?> dc) {
		int i = 0;
		if (dc instanceof DomainConceptEQ) {
			i = INTEQ;
		} else if (dc instanceof DomainConceptLT) {
			i = INTLT;
		} else if (dc instanceof DomainConceptLTEQ) {
			i = INTLTEQ;
		}
		return i;
	}

	/**
	 * Find all relationships between any two concrete elements, e.g., 3<4, 4<5
	 * 1<2
	 */
	public HashSet<DomainConcept<?, ?>> buildDomainConcepts(HashSet<String> ss) {
		HashSet<DomainConcept<?, ?>> dcs = new HashSet<DomainConcept<?, ?>>();
		while (!ss.isEmpty()) {
			String s = ss.iterator().next();
			ss.remove(s);
			for (String c : ss) {
				int res = s.compareTo(c);
				if (res == 0) {
					dcs.add(new DomainConceptEQ<String, String>(s, c));
				} else if (res > 0) {
					dcs.add(new DomainConceptLT<String, String>(c, s));
				} else if (res < 0) {
					dcs.add(new DomainConceptLT<String, String>(s, c));
				}
			}
		}
		// if(ss.size()>1){
		// dcs.addAll(buildDomainConcepts(ss));
		// }

		return dcs;
	}

}
